# -*- coding: utf-8 -*-
"""
JWT认证管理

处理JWT token的生成、验证和管理：
- 生成access token和refresh token
- 验证token有效性
- 解析用户信息
- 处理token过期和刷新
"""

from jose import jwt
from datetime import datetime, timedelta
from typing import Optional, Dict, Any
from fastapi import HTTPException, status, Depends, Request
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from passlib.context import CryptContext
import logging

from ..core.config import get_settings
from ..core.exceptions import AuthenticationError, AuthorizationError
from ..models.rbac import User

logger = logging.getLogger(__name__)
settings = get_settings()

# 密码加密上下文
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

# HTTP Bearer认证
security = HTTPBearer()


class JWTManager:
    """JWT管理器"""
    
    def __init__(self):
        self.secret_key = settings.SECRET_KEY
        self.algorithm = settings.ALGORITHM
        self.access_token_expire_minutes = settings.ACCESS_TOKEN_EXPIRE_MINUTES
    
    def create_access_token(self, data: Dict[str, Any], expires_delta: Optional[timedelta] = None) -> str:
        """创建访问令牌"""
        to_encode = data.copy()
        
        if expires_delta:
            expire = datetime.utcnow() + expires_delta
        else:
            expire = datetime.utcnow() + timedelta(minutes=self.access_token_expire_minutes)
        
        to_encode.update({"exp": expire, "type": "access"})
        
        try:
            encoded_jwt = jwt.encode(to_encode, self.secret_key, algorithm=self.algorithm)
            logger.info(f"创建访问令牌成功: user_id={data.get('sub')}")
            return encoded_jwt
        except Exception as e:
            logger.error(f"创建访问令牌失败: {e}")
            raise AuthenticationError("令牌创建失败")
    
    def create_refresh_token(self, data: Dict[str, Any]) -> str:
        """创建刷新令牌"""
        to_encode = data.copy()
        expire = datetime.utcnow() + timedelta(days=30)  # 刷新令牌30天有效期
        
        to_encode.update({"exp": expire, "type": "refresh"})
        
        try:
            encoded_jwt = jwt.encode(to_encode, self.secret_key, algorithm=self.algorithm)
            logger.info(f"创建刷新令牌成功: user_id={data.get('sub')}")
            return encoded_jwt
        except Exception as e:
            logger.error(f"创建刷新令牌失败: {e}")
            raise AuthenticationError("刷新令牌创建失败")
    
    def verify_token(self, token: str, token_type: str = "access") -> Dict[str, Any]:
        """验证令牌"""
        try:
            payload = jwt.decode(token, self.secret_key, algorithms=[self.algorithm])
            
            # 检查令牌类型
            if payload.get("type") != token_type:
                raise AuthenticationError("令牌类型错误")
            
            # 检查过期时间
            if payload.get("exp") < datetime.utcnow().timestamp():
                raise AuthenticationError("令牌已过期")
            
            return payload
            
        except jwt.ExpiredSignatureError:
            logger.warning("令牌已过期")
            raise AuthenticationError("令牌已过期")
        except jwt.JWTError as e:
            logger.warning(f"令牌验证失败: {e}")
            raise AuthenticationError("无效的令牌")
        except Exception as e:
            logger.error(f"令牌验证异常: {e}")
            raise AuthenticationError("令牌验证失败")
    
    def refresh_access_token(self, refresh_token: str) -> str:
        """使用刷新令牌获取新的访问令牌"""
        try:
            # 验证刷新令牌
            payload = self.verify_token(refresh_token, "refresh")
            
            # 创建新的访问令牌
            new_token_data = {
                "sub": payload.get("sub"),
                "username": payload.get("username"),
                "permissions": payload.get("permissions", [])
            }
            
            return self.create_access_token(new_token_data)
            
        except Exception as e:
            logger.error(f"刷新访问令牌失败: {e}")
            raise AuthenticationError("令牌刷新失败")


class PasswordManager:
    """密码管理器"""
    
    @staticmethod
    def hash_password(password: str) -> str:
        """加密密码"""
        return pwd_context.hash(password)
    
    @staticmethod
    def verify_password(plain_password: str, hashed_password: str) -> bool:
        """验证密码"""
        return pwd_context.verify(plain_password, hashed_password)
    
    @staticmethod
    def validate_password_strength(password: str) -> bool:
        """验证密码强度"""
        if len(password) < settings.PASSWORD_MIN_LENGTH:
            return False
        
        # 检查是否包含数字
        if not any(c.isdigit() for c in password):
            return False
        
        # 检查是否包含字母
        if not any(c.isalpha() for c in password):
            return False
        
        return True


# 全局实例
jwt_manager = JWTManager()
password_manager = PasswordManager()


async def get_current_user(
    credentials: HTTPAuthorizationCredentials = Depends(security),
    request: Request = None
) -> Dict[str, Any]:
    """获取当前认证用户"""
    try:
        # 验证令牌
        payload = jwt_manager.verify_token(credentials.credentials)
        
        # 提取用户信息
        user_info = {
            "user_id": payload.get("sub"),
            "username": payload.get("username"),
            "permissions": payload.get("permissions", []),
            "exp": payload.get("exp")
        }
        
        # 将用户信息存储到请求状态中（用于日志记录）
        if request:
            request.state.user = user_info
        
        return user_info
        
    except AuthenticationError:
        raise
    except Exception as e:
        logger.error(f"获取当前用户失败: {e}")
        raise AuthenticationError("用户认证失败")


async def get_current_active_user(
    current_user: Dict[str, Any] = Depends(get_current_user)
) -> Dict[str, Any]:
    """获取当前活跃用户"""
    # 这里可以添加额外的用户状态检查
    # 比如检查用户是否被禁用等
    return current_user


def require_permissions(required_permissions: list):
    """权限检查装饰器"""
    def permission_checker(current_user: Dict[str, Any] = Depends(get_current_user)):
        user_permissions = current_user.get("permissions", [])
        
        # 检查是否具有所需权限
        if not all(perm in user_permissions for perm in required_permissions):
            missing_perms = [perm for perm in required_permissions if perm not in user_permissions]
            logger.warning(f"权限不足: user_id={current_user.get('user_id')}, missing={missing_perms}")
            raise AuthorizationError(f"缺少权限: {', '.join(missing_perms)}")
        
        return current_user
    
    return permission_checker


def require_admin():
    """要求管理员权限"""
    return require_permissions(["admin"])


def require_customer_access():
    """要求客户管理权限"""
    return require_permissions(["customer_read", "customer_write"])


# 常用权限检查函数
async def check_customer_permission(
    current_user: Dict[str, Any] = Depends(get_current_user)
) -> Dict[str, Any]:
    """检查客户管理权限"""
    return require_permissions(["customer_read"])(current_user)


async def check_admin_permission(
    current_user: Dict[str, Any] = Depends(get_current_user)
) -> Dict[str, Any]:
    """检查管理员权限"""
    return require_permissions(["admin"])(current_user)